/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package ru.niir.goodfil.ui;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import ru.niir.goodfil.audit.domain.OrderUIBuildEvent;
import ru.niir.goodfil.audit.domain.enums.OrderUIBuildAction;
import ru.niir.goodfil.db.*;
import ru.niir.goodfil.db.dic.ClientState;
import ru.niir.goodfil.db.dic.OrderState;
import ru.niir.goodfil.exceptions.GoodfilException;
import ru.niir.goodfil.exceptions.GoodfilSecurityException;
import ru.niir.goodfil.exceptions.MailerException;
import ru.niir.goodfil.exceptions.ValidationException;
import ru.niir.goodfil.fun.ISSignature;
import ru.niir.goodfil.utils.ExportStringFormatter;
import ru.niir.goodfil.utils.PriceInfo;

import javax.faces.model.SelectItem;
import java.math.BigDecimal;
import java.sql.*;
import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.*;

import static ru.niir.goodfil.utils.Utils._assert;
import static ru.niir.goodfil.utils.Utils._assertNotNull;

/**
 * Бин для страницы просмотра заказа кладовщика. Эта страница (StorekepeerOrderPage.jsf) и этот бин используются как для
 * сборки заказа, так и для его выдачи. Поле StorekeeperOrderBean#mode отвечает за то, в каком режиме функционирует бин:
 * в режиме выдачи или сборки.
 */
public class StorekeeperOrderBean extends PageBean {
    /**
     * Значение по умолчанию для поля mode
     */
    private static final int MODE_UNDEFINED = 0;

    /**
     * Режим, в котором клдовщик собирает заказ, но не выдает его
     */
    private static final int MODE_BUILD = 1;

    /**
     * Режим, в котором кладовщик выдает заказ после сборки
     */
    private static final int MODE_OUT = 2;

    private static Logger logger = Logger.getLogger(StorekeeperOrderBean.class);

    private Order order;

    private List<OrderItem> orderItems;

    private List<SelectItem> storeKeepers;

    private Map<Long, String> factGivenMap;

    public static final String BEAN_NAME = "StorekeeperOrderBean";

    public static final String LIST_PAGE = "sk1";

    public static final String LIST_BEAN_NAME = "StorekeeperOrdersBean";

    /**
     * Режим, в котором функционирует бин: сборка или выдача.
     */
    private int mode = MODE_UNDEFINED;

    public void initialize(long orderId) {
        //  Доступ на страницу заказов разрешен только администраторам и кладовщикам
        if (!(getSecurityBean().getIsRoleAdministrator() ||
                getSecurityBean().getIsRoleStoreman())) {
            throw new GoodfilSecurityException("Доступ на страницу заказов разрешен только администраторам, менеджерам и кладовщикам");
        }

        order = gf().getOrderById(orderId);

        if (order.isDeleted()) {
            logger.warn("Attemp to storekeep deleted or blank 1C order");
            sendRedirect("StorekeeperOrdersPage.jsf");
            return;
        }

        order.setClient(gf().getClientById(order.getClientId()));
        order.setManager(gf().getUserById(order.getManagerId()));
        gf().addOrderInfo(order);

        factGivenMap = new LinkedHashMap<Long, String>();
        orderItems = gf().getOrderItemsByOrderId(orderId);
        for (OrderItem orderItem : orderItems) {
            Good good = gf().getGoodById(orderItem.getGoodId());
            orderItem.setGood(good);
            factGivenMap.put(orderItem.getId(), String.valueOf(orderItem.getCount()));
        }

        Collections.sort(orderItems, new Comparator<OrderItem>() {
            public int compare(OrderItem o1, OrderItem o2) {
                return o1.getGood().getCode().compareTo(o2.getGood().getCode());
            }
        });

        //  Установка соответствующего режима работы бина в зависимости от текущего статуса заказа
        if (order.getOrderStateId() == OrderState.OUTPUT) {
            mode = MODE_BUILD;
        }
        if (order.getOrderStateId() == OrderState.DELIVERY) {
            mode = MODE_OUT;
        }
        mode = MODE_BUILD;
        storeKeepers = new ArrayList<SelectItem>();
        storeKeepers.add(new SelectItem(-1, "Не выбрано"));
        for(User user : gf().getStoreKeepers()){
            if(user.getId() == 126 || !user.isActive()) continue;
            storeKeepers.add(new SelectItem(user.getId(), user.getFio()));
        }
    }

    /**
     * Возвращает true, если страница открыта для СБОРКИ заказа.
     */
    public boolean getIsBuildMode() {
        _assert(mode != MODE_UNDEFINED);
        return mode == MODE_BUILD;
    }

    /**
     * Возвращает true, если страница открыта для ВЫДАЧИ заказа.
     */
    public boolean getIsOutMode() {
        _assert(mode != MODE_UNDEFINED);
        return mode == MODE_OUT;
    }

    public void release() {
        this.order = null;
        this.orderItems = null;
    }

    /**
     * Обработчик нажатия на кнопку "Готово".
     */
    public void commit() throws ValidationException {
        _assert(mode != MODE_UNDEFINED);

        if (mode == MODE_BUILD) {
            // В режиме сборки требуется сохранить кол-во собранного товара,
            // отправить менеджеру уведомление об изменениях и изменить
            // статус заказа
            commitInBuildMode();
        }
        if (mode == MODE_OUT) {
            // В режиме выдачи ничего делать не требуется
            commitInOutMode();
        }

        if (hasFacesErrors()) {
            return;
        }

        // Возвращаемся к списку заказов
        release();
        StorekeeperOrdersBean bean = (StorekeeperOrdersBean) getBean(LIST_BEAN_NAME);
        bean.refresh();
        sendRedirect(LIST_PAGE);
    }

    /**
     * Функция обрабатывает нажатие кнопки "Готово" для режима выдачи заказа.
     */
    public void commitInOutMode() {
        // В режиме выдачи требуется изменить статус заказа
        gf().setOrderState_Unsafe(order.getId(), OrderState.COMPLETE);

        logOrderUIBuildEvent(order, OrderUIBuildAction.OUT);
    }

    /**
     * Функция обрабатывает нажатие кнопки "Готово" для режима сборки заказа.
     */
    public void commitInBuildMode() throws ValidationException {
        // В режиме сборки требуется сохранить кол-во собранного товара,
        // отправить менеджеру уведомление об изменениях и изменить
        // статус заказа

        if(!getIsCanCommitOrders()){
            sendRedirect("sk1");
            System.out.println("Подтвердить нельзя, идет выгрузка остатков");
            return;
        }

        List<String> managerMassages = new ArrayList<String>();
        List<Good> goods = new ArrayList<Good>();

        for (OrderItem orderItem : orderItems) {
            String factGiven = factGivenMap.get(orderItem.getId());
            boolean validationResult = validateFactGiven(orderItem, factGiven);
            if (validationResult) {
                orderItem.setFactGivenFromStore(new Integer(factGiven));
                if (orderItem.getCount() != orderItem.getFactGivenFromStore()) {
                    managerMassages.add(orderItem.getGood().getCode() + ": " + orderItem.getCount() + " -> " + orderItem.getFactGivenFromStore());
                }
            }
            Good good = gf().getGoodById(orderItem.getGoodId());
            if(good.getReservedCount() >= orderItem.getCount() && good.getCount() >= orderItem.getFactGivenFromStore()){
                good.setReservedCount(good.getReservedCount() - orderItem.getCount());
                good.setCount(good.getCount() - orderItem.getFactGivenFromStore());
                good.setStorageCount(good.getStorageCount() - orderItem.getFactGivenFromStore());
                //gf().updateGood(good);
                goods.add(good);
            }
            else {
                facesError("Ошибка обновления данных для товара с артикулом " + good.getCode() + ". Свяжитесь с администратором.");
                return;
            }
        }

        if (hasFacesErrors()) {
            return;
        }

        int sum = 0;
        for (OrderItem orderItem : orderItems) {
            sum += orderItem.getFactGivenFromStore();
        }
        if (sum == 0) {
           // facesError("Необходимо выдать ненулевое количество товара");
            deleteOrder(order, managerMassages);
            return;
        }

        if(order.getStorekeeperId() == -1 || order.getStorekeeperId() == 0){
            facesError("Необходимо выбрать ответственного за сборку");
        }

        if (hasFacesErrors()) {
            return;
        }

        order.setAssemblyDate(Calendar.getInstance().getTime());
        gf().updateOrder(order);
        gf().updateOrderItems(orderItems);
        gf().updateGoods(goods);



        logOrderUIBuildEvent(order, OrderUIBuildAction.BUILD);

        // Устанавливаем новый статус заказа в зависимости от даты отгрузки:
        // либо "выдача" либо "ожидание выдачи"
        if (order.getDeliveryDate() == null) {
            gf().setOrderState_Unsafe(order.getId(), OrderState.AWAITING_DELIVERY);
        } else {
            gf().setOrderState_Unsafe(order.getId(), OrderState.DELIVERY);
        }
/*        if(gf().getIsReservedOn()){
            try {
                gf().debitOrderItems(order.getId(), gf().getOrderById(order.getId()).getOrderStateId());
            } catch (BatchUpdateException e) {
                facesError("Произошла ошибка. Попробуйте позже или свяжитесь с администратором. Текст ошибки: " + e.getMessage());
                logger.error(e.getMessage());
            } catch (GoodfilException e) {
                facesError("Произошла ошибка. Попробуйте позже или свяжитесь с администратором. Текст ошибки: " + e.getMessage());
                logger.error(e.getMessage());
            }
        }*/

        //  Отправить менеджеру уведомление
        if (managerMassages.size() > 0) {
            try {
                long managerId = order.getManagerId();
                long clientId = order.getClientId();
                User manager = gf().getUserById(managerId);
                Client client = gf().getClientById(clientId);
                String fio = manager.getFio();
                String email = manager.getEmail();

                String subject = "Склад внес изменения в заказ " + order.getIdAsString();
                String text = "Уважаемый(-ая) " + fio + "!\n" +
                        "Ваш заказ под номером " + order.getIdAsString() + " (номер накладной " + order.getC1Num() + ", клиент: " + client.getCompanyName() + ", дата создания заказа: " + order.getCreationDateAsString() + ") собран на сладе (дата отгрузки: " + order.getDeliveryDateAsString() + ")." +
                        "Некоторые позиции заказа были изменены:\n" +
                        StringUtils.join(managerMassages, "\n") +
                        ISSignature.get();

                try {
                    gf().sendEmailMsg(email, subject, text);
                } catch (MailerException e) {
                    e.printStackTrace();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        //  Отправить изменения в 1С или они отправятся автоматически через OrderHandler?
        //  Отправлять изменения нужно. В других местах используется managed setOrderStateId
        //  Теперь изменения нужно в любом случае отправлять
//        if (managerMassages.size() > 0) {
        DataExchange de = new DataExchange();
        if(gf().getIsReservedOn()){
            de.setTableName("Orders");
        }
        else {
            de.setTableName("OrderBuild");
        }
        de.setItemId(order.getId());
        gf().addDataExchange(de);
        Calendar now = Calendar.getInstance();
        now.set(Calendar.HOUR_OF_DAY, 0);
        now.set(Calendar.MINUTE, 0);
        now.set(Calendar.SECOND, 0);
        now.set(Calendar.MILLISECOND, 0);
        if (now.get(Calendar.DAY_OF_WEEK) == Calendar.FRIDAY) {
            now.add(Calendar.DATE, 3);
        } else if (now.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY) {
                now.add(Calendar.DATE, 2);
        } else if (order.getClient().getClientStateId() != ClientState.E_SHOP_DAY_TO_DAY) {
                now.add(Calendar.DATE, 1);
        }

        gf().saveStorageDeliveryStatus(new StorageDeliveryStatus(order.getId(), order.getDeliveryDateIsInPast() ? now.getTime() : order.getDeliveryDate(), order.getDeliveryTypeId(), order.getClient().getClientStateId()));

    }

    private void logOrderUIBuildEvent(Order order, OrderUIBuildAction action) {
        Client client = order.getClient();
        if (client == null) {
            client = gf().getClientById(order.getClientId());
        }

        User manager = order.getManager();
        if (manager == null) {
            manager = gf().getUserById(order.getClientId());
        }

        OrderUIBuildEvent event = getAuditHelper().createEvent(OrderUIBuildEvent.class);
        event.setClientId(String.valueOf(client.getId()));
        event.setClientName(client.getCompanyName());
        event.setC1Num(order.getC1Num());
        event.setManagerId(String.valueOf(order.getManagerId()));
        event.setManagerName(manager.getFio());
        event.setOrderId(String.valueOf(order.getId()));
        event.setAction(action);

        SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss");
        if (order.getCreationDate() != null) {
            try {
                event.setCreationDate(sdf.format(order.getCreationDate()));
            } catch (Exception e) {
                event.setCreationDate(order.getCreationDate().toString());
            }
        }

        if (order.getDeliveryDate() != null) {
            try {
                event.setDeliveryDate(sdf.format(order.getDeliveryDate()));
            } catch (Exception e) {
                event.setDeliveryDate(order.getDeliveryDate().toString());
            }
        }

//        getAuditService().logOrderUIBuild(event);
    }

    public void cancel() {
        release();
        StorekeeperOrdersBean bean = (StorekeeperOrdersBean) getBean(LIST_BEAN_NAME);
        bean.refresh();
        sendRedirect(LIST_PAGE);
    }

    public Order getOrder() {
        if (order == null) {
            sendRedirect(LIST_PAGE);
        }
        return order;
    }

    public List<OrderItem> getOrderItems() {
        if (orderItems == null) {
            sendRedirect(LIST_PAGE);
        }
        return orderItems;
    }

    public List<OrderItem> getActualOrderItems(){
        List<OrderItem> orderItemList = gf().getOrderItemsByOrderId(order.getId());
        for(OrderItem orderItem : orderItemList){
            orderItem.setGood(gf().getGoodById(orderItem.getGoodId()));
        }
        Collections.sort(orderItemList, new Comparator<OrderItem>() {
            public int compare(OrderItem o1, OrderItem o2) {
                return o1.getGood().getCode().compareTo(o2.getGood().getCode());
            }
        });
        return orderItemList;
    }

    public boolean validateFactGiven(OrderItem orderItem, String factGivenValue) {
        try {
            int ivalue = Integer.parseInt(factGivenValue);
            if (ivalue < 0 || ivalue > orderItem.getCount()) {
                return facesError("Значение поля \"Выдано\" неверно заполнено для товара " + orderItem.getGood().getCode());
            }
            return true;
        } catch (NumberFormatException nfe) {
            return facesError("Значение поля \"Выдано\" неверно заполнено для товара " + orderItem.getGood().getCode());
        }
    }

    public Map<Long, String> getFactGivenMap() {
        return factGivenMap;
    }

    public String getOrder1CString() {
        Order order = getOrder();

        Client client = gf().getClientById(order.getClientId());
        order.setClient(client);

        User manager = gf().getUserById(order.getManagerId());
        order.setManager(manager);

        List<OrderItem> orderItems = gf().getOrderItemsByOrderId(order.getId());
        for (OrderItem oi : orderItems) {
            oi.setGood(gf().getGoodById(oi.getGoodId()));
        }

        return new ExportStringFormatter(order, orderItems).formatExportString();
    }

    public String getOrder1CStringForStorekeeper(){
        Order order = getOrder();
        List<OrderItem> orderItems = gf().getOrderItemsByOrderId(order.getId());
        return new ExportStringFormatter(order, orderItems).formatExportStringForStorekeeper();
    }

    /**
     * Возвращает сумму товаров.
     *
     * @return сумма товаров
     */
    public float getOrderPrice() {
        _assertNotNull(getOrderItems());

        float result = 0;
        for (OrderItem orderItem : getOrderItems()) {
            result += orderItem.getSum();
        }
        return new BigDecimal(String.valueOf(result)).setScale(2, BigDecimal.ROUND_HALF_UP).floatValue();
    }

    public List<SelectItem> getStoreKeepers() {
        return storeKeepers;
    }

    public void deleteOrder(Order order, List<String> managerMassages){
        long managerId = order.getManagerId();
        long clientId = order.getClientId();
        User manager = gf().getUserById(managerId);
        Client client = gf().getClientById(clientId);
        String subject = "Склад внес изменения в заказ " + order.getIdAsString();
        String managerMessage = "Уважаемый(-ая) " + manager.getFio() + "!\n" +
                "Ваш заказ под номером " + order.getIdAsString() + " (номер накладной " + order.getC1Num() + ", клиент: " + client.getCompanyName() + ", дата создания заказа: " + order.getCreationDateAsString() + ") не может быть собран, так как на складе нет ни одной из заказынных позиций. " +
                "Заказ автоматически удален из ИС."+ "\n"+
                ISSignature.get();
        String adminMail = gf().get("admin.mail");
        String header = "Нет товара на складе";
        String adminMessage = "Уважаемый Администратор ИС!\n" +
                "\n" +
                "На складе нет следующих позиций:\n" +
                StringUtils.join(managerMassages, "\n") + "\n" +
                "Необходимо поставить задачу кассе на внесение корректировок в 1С. " + "\n" +
                                ISSignature.get();
        try {
            gf().sendEmailMsg(manager.getEmail(), subject, managerMessage);
            gf().sendEmailMsg(adminMail, header, adminMessage);
            gf().softRemoveOrder(order.getId());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private boolean getIsCanCommitOrders(){
        int canCommit = gf().getIntOrCreateIfNotExists("can.storage.confirm.orders", 0, "Можно подтверждать заказы");
        return canCommit == 1;
    }

    public void orderPrint(){
        gf().setOrderState_Unsafe(order.getId(), OrderState.ACCEPTED_BY_STORAGE);
    }

}

